/*	--- UDP编程 --- 

和TCP编程相比，UDP编程就简单得多，因为UDP没有创建连接，数据包也是一次收发一个，所以没有流的概念。

在Java中使用UDP编程，仍然需要使用Socket，因为应用程序在使用UDP时必须指定【网络接口（IP）】和【端口号】。

注意：【UDP端口】和【TCP端口】虽然都使用0~65535，但他们是【两套独立的端口】，即一个应用程序用TCP占用了【端口1234】，不影响另一个应用程序用UDP占用【端口1234】。


-------------------------------------


#	服务器端 Server

在服务器端，使用UDP也需要监听指定的端口。

Datagram 数据包、数据报文
Java提供了DatagramSocket来实现这个功能，代码如下：  */


DatagramSocket ds = new DatagramSocket(6666); //监听本机の端口

for (;;) {	// 无限循环
	byte[] buffer = new byte[1024]; // 数据缓冲区
	// DatagramPacket 数据报封包;数据报文包
	DatagramPacket packet = new DatagramPacket(buffer, buffer.length); // DatagramPacket的缓冲区设置

	ds.receive(packet); // DatagramSocket.receive(DatagramPacket)

	// 收取到的数据存储在buffer中, 由packet.getOffset(), packet.getLength()指定 起始位置、长度
	String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);

	// 发送的数据
	byte[] data = "Send the data".getBytes(StandardCharsets.UTF_8);

	packet.setData(data); // DatagramPacket.setData(byte[] data)
	ds.send(packet); // DatagramSocket.send(DatagramPacket.setData(byte[] data));
}



// 服务器端Server, 首先使用如下语句在指定的端口监听UDP数据包
DatagramSocket ds = new DatagramSocket(6666);


// 如果没有其他应用程序占据这个端口,那么监听成功,我们就是用一个无限循环来处理收到的UDP DatagramPacket
for (;;) {
	//... 
}



// 要接收一个UDP数据包，需要准备一个byte[]缓冲区，并通过DatagramPacket实现接收：
byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);



// 假设我们收取到的是一个String，那么，通过DatagramPacket返回的packet.getOffset()和packet.getLength()确定数据在缓冲区的起止位置：
String s = new String(packet.getData(), packet.getOffset(), packet.getLength(), StandardCharsets.UTF_8);
	// offset  n.  开端,出发


/*
当服务器收到一个DatagramPacket后，通常必须立刻回复一个或多个UDP包，因为客户端地址在DatagramPacket中，每次收到的DatagramPacket可能是不同的客户端，如果不回复，客户端就收不到任何UDP包。

发送UDP包也是通过DatagramPacket实现的，发送代码非常简单：*/
byte[] data = ...;
packet.setData(data);
ds.send(packet);



/*===================================================


#	客户端 Client


和服务器端相比，客户端使用UDP时，只需要直接向服务器端发送UDP包，然后接收返回的UDP包：*/
DatagramSocket ds = new DatagramSocket(); // Client不需要指定port

ds.setSoTimeout(1000); // 设定暂停时间
ds.connect(InetAddress.getByName("localhost"), 6666);
	// connect指定服务器域名和端口

// 发送, 3步
byte[] data = "Hello".getBytes(); // 建立【输出缓冲区】
DatagramPacket packet = new DatagramPacket(data, data.length);
ds.send(packet); // send

// 接受, 3步
byte[] buffer = new byte[1024]; // 划分【接收缓冲区】
packet = new DatagramPacket(buffer, buffer.length);//重建【数据包】
ds.receive(packet); // receive
String resp = new String(packet.getData(), packet.getOffset(), packet.getLength()); // 数据包.获取数据, .获取起始点, .获取长度


ds.disconnect(); // DatagramSocket.disconnect()





// 客户端Client打开一个DatagramSocket使用以下代码:
DatagramSocket ds = new DatagramSocket();
ds.setSoTimeout(1000);
ds.connect(InetAddress.getByName("localhost"), 6666);


/*
客户端创建DatagramSocket实例时并不需要指定端口，而是由操作系统自动指定一个当前未使用的端口。紧接着，调用setSoTimeout(1000)设定超时1秒，意思是后续接收UDP包时，等待时间最多不会超过1秒，否则在没有收到UDP包时，客户端会无限等待下去。这一点和服务器端不一样，服务器端可以无限等待，因为它本来就被设计成长时间运行。

注意到客户端的DatagramSocket还调用了一个connect()方法“连接”到指定的服务器端。不是说UDP是无连接的协议吗？为啥这里需要connect()？

这个connect()方法不是真连接，它是为了在客户端的DatagramSocket实例中保存服务器端的IP和端口号，确保这个DatagramSocket实例只能往指定的地址和端口发送UDP包，不能往其他地址和端口发送。这么做不是UDP的限制，而是Java内置了安全检查。

如果客户端希望向两个不同的服务器发送UDP包，那么它必须创建两个DatagramSocket实例。

后续的收发数据和服务器端是一致的。通常来说，客户端必须先发UDP包，因为客户端不发UDP包，服务器端就根本不知道客户端的地址和端口号。

如果客户端认为通信结束，就可以调用disconnect()断开连接：*/
ds.disconnect();

/* 注意到disconnect()也不是真正地断开连接，它只是清除了客户端DatagramSocket实例记录的远程服务器地址和端口号，这样，DatagramSocket实例就可以连接另一个服务器端。




====================================================*/



/*-----------------------------------


#	----- UDP编程 の 小结 ----- 

使用UDP协议通信时，服务器和客户端双方无需建立连接：

	· 服务器端Server用DatagramSocket(port)监听端口；
	· 客户端Client使用DatagramSocket.connect()指定远程地址和端口；
	· 双方(Server,Client)通过receive()和send()读写数据；
	· DatagramSocket没有IO流接口，数据被直接写入byte[]缓冲区。




*/









